# -*- coding: utf-8 -*-
# -*- frozen_string_literal: true -*-
require "lorca/version"

begin
  gem "sysrandom"
  require "sysrandom/securerandom"
rescue Gem::LoadError
  require "securerandom"
end

# Lorca main class.
class Lorca
  # Exception raised by an invalid number of dice
  LorcaDiceError = Class.new StandardError

  # Exception raised by an invalid range
  LorcaRangeError = Class.new StandardError

  # Exception raised by an invalid counter
  LorcaCounterError = Class.new StandardError

  @settings = {}

  # Lorca core methods.
  module LorcaCore
    # LorcaCore class methods.
    module LorcaExtension
      class << self
        # Expose warn as it is used for deprecation warnings.
        # Lorca::LorcaCore::LorcaExtension.warn can be overridden for custom
        # extension warnings.
        public :warn
      end

      # Lorca class settings/configuration hash.
      attr_reader :settings

      # Adds a Lorca Expansion. An expansion is simply a module which changes
      # Lorca. Methods defined under the +ExpansionName::LorcaPlugin+ namespace
      # get included. The ones defined under +ExpansionName::LorcaExtension+,
      # extended.
      #
      # Expansions can define +ExpansionName.load_dependencies+ to load, and
      # configure their dependencies. They can also define +ExpansionName.configure+
      # so users can modify its settings.
      #--
      # don't load from load_path; security over convenience
      #++
      def add expansion, **stns, &block
        expansion.load_dependencies(self, **stns, &block) if expansion.respond_to?(:load_dependencies)
        include expansion::LorcaPlugin if defined? expansion::LorcaPlugin
        extend expansion::LorcaExtension if defined? expansion::LorcaExtension
        expansion.configure(self, **stns, &block) if expansion.respond_to?(:configure)
        self
      end
    end

    # LorcaCore instance methods.
    module LorcaPlugin
      # Lorca instance settings hash. Frozen duplicate of class settings meant
      # for reading expansion configurations.
      def settings
        self.class.settings.dup.freeze
      end

      # Roll a word id using given number of +dice+.
      def roll_word_id(dice:)
        dice = validate_counter dice, range: dice..dice
        id = ""
        dice.times { id += roll_dice.to_s }
        id
      rescue Lorca::LorcaCounterError => e
        raise Lorca::LorcaDiceError, e.message
      end

      # Roll a six-sided dice.
      def roll_dice
        (SecureRandom.random_number(5) + 1)
      end

      # Validate a given counter +number+ is within the given +range+.
      def validate_counter(number, range:)
        num = validate_positive_integer number
        return num if num == range.min && num == range.max

        range = validate_range range
        range.cover?(num) ? num : fail(LorcaCounterError, "Counter out of range")
      end

      # Validate the +range+ given is end-inclusive, and only positive numbers.
      def validate_range range
        validate_positive_integer range.min
        validate_positive_integer range.max
        range
      rescue Lorca::LorcaCounterError
        raise LorcaRangeError, "Should be an end-inclusive range of positive integers"
      end

      # Validate +counter+ is a number greater than zero.
      def validate_positive_integer counter
        unless counter.is_a?(Integer) && counter > 0
          fail LorcaCounterError, "Should be a positive integer"
        end
        counter
      end
    end
  end

  extend LorcaCore::LorcaExtension
  add LorcaCore
end
